<?php	
/**
 * Smernel.php
 * 
 * @version 0v0r00
 * @package Smernel
 */
	class Smernel{
		
		/**
		 * Whether the domain is done constructing
		 * @var bool
		 */
		private $dom_up = false;
		
		/**
		 * instantiated domains
		 * @var array
		 */
		private $domains = array();
		
		/**
		 * instantiated proxies
		 * @var array
		 */
		private $proxies = array();
		
		/**
		 * instantiated database abstraction layers
		 * @var array
		 */
		private $als = array();
		
		/**
		 * Smernel Objects of which there should be only one instance ("singletons").
		 * @var array
		 */
		private $SMO = array();
		
		/**
		 * Unique instance objects; as above but indexed by full path
		 * @var array
		 */
		private $UIO = array();
		
		/**
		 * Current working directory
		 * @var Fmgr_Directory
		 */
		private $cwd;
		
		/**
		 * Callback functions to be called at given events (such as header output)
		 * @var array
		 */
		private $event_funcs = array();
		
		/**
		 * SQL query counter
		 * @var integer
		 */
		public $MQC = 0;
		
		/**
		* Constructor.
		* Includes global and contextual function sets. Precedes Domain()->run()
		*/
		function __construct(){
			require_once SMERNEL_PATH.'/funcset-global.php';
			require_once SMERNEL_PATH.'/funcset-'.CONTEXT.'.php';
			define('SERVER_RDK', (PUBLIC_DOMAIN ? 'docs' : 'desk')); 
			define('SERVER_SMRF', DOM_ID.'::'.SERVER_RDK.':/');
			sm_set_error_handler('sm_error_handler');
			set_error_handler("call_error_handler");
		}
		
####	WORKING DIRECTORY
		
		/**
		* Changes cwd to given directory
		* @param string|Fmgr_Directory
		* @returns bool
		*/
		function chdir($d){
			if(!smuoi_is_dir($d) && (!is_string($d) || (!$d = smrf($d)))) return false; 
			if(!smuoi_is_executable($d)) return false;
			chdir($d->realpath());
			$this->cwd = $d;
			return true;
		}
		
		/**
		* {@link Smernel::chdir} should be called at least once before this is reied upon
		* @returns Fmgr_Directory
		*/
		function cwd(){
			if(!$this->cwd) $this->chdir(SERVER_SMRF);
			return $this->cwd;
		}
		
		/**
		* Current working directory's smrf
		* @returns string
		*/
		function getcwd(){
			return (!$this->cwd) ? SERVER_SMRF : $this->cwd->smrf();	
		}
		
		/**
		* Current working domain
		* @returns Domain
		*/
		function cwdom(){
			return Domain($this->getcwdom());
		}
		
		/**
		* Current working domain id
		* @returns string
		*/
		function getcwdom(){
			return (!$this->cwd) ? DOM_ID : $this->cwd->dom_id();
		}			
		
####	DOMAINS		
		
		/**
		* to be called by active domain on instantiation
		* @returns bool
		*/ 
		function dom_up($set=false){
			return !$set ? $this->dom_up : $this->dom_up = true;
		}
		
		/**
		* domain instantiator
		* @param string id of domain
		* @returns Domain
		*/ 
		function domain($D=DOM_ID){
			if(!isset($this->domains[$D])){
				if(!is_domain($D)) return false;
				$this->domains[$D] = (PUBLIC_DOMAIN) ? new Domain($D) : new DesktopDomain($D);
			}
			return $this->domains[$D];
		}
		
####	PROXIES AND ABSTRACTORS
		
		/**
		* proxy instantiator
		* @param string id of domain
		* @returns dataProxy
		*/ 
		function proxy($D=DOM_ID){
			if(!isset($this->proxies[$D])){
				if(!is_database($D)) return false;
				$this->proxies[$D] = (($D == 'root') || !usurp()) ? new dataProxy($D) : new dataUsurpa($D);
			}
			return $this->proxies[$D];
		}
		
		/**
		* database abstractor instantiator
		* @param string type
		* @param string user
		* @param string pass
		* @param string database
		* @param string host
		* @returns dba_mysql
		*/ 
		function call_abstractor($type, $user, $pass, $database, $host){
			if(isset($this->als[$database.$user])) return $this->als[$database.$user]->check_pass($pass); 	
			$class = 'dba_'.$type;
			return $this->als[$database.$user] = new $class($user, $pass, $database, $host);
		}
		
		/**
		* not sure what this is for
		*/ 
		function package_path($p, $D=DOM_ID){
			if(!$px = $this->proxy($D)) return false;
			return (!$pd = $px->package_data(strtolower($p))) ? false : $pd->path;
		}
		
####	UNIQUE CLASSES	
	
		/**
		* getObj (SMO) : Pass classname only
		* @returns object
		*/
		function getObj($class){
			if(!$this->is_invoked($class)) $this->SMO[$class] = $this->instantiate(SMERNEL_PATH."/$class");
			return $this->SMO[$class];	
		}
	
		/**
		* UIO : Unique instance objects; as SMO but pass full path minus .php extension
		* @returns object
		*/
		function UIO($path){
			if(!$this->is_invoked($path, 'UIO')) $this->UIO[$path] = $this->instantiate($path);
			return $this->UIO[$path];
		}
	
		/**
		* Whether "singleton" object that lives here has been invoked
		* @returns bool
		*/
		function is_invoked($class, $k='SMO'){
			return array_key_exists($class, $this->$k);
		}
		
		/**
		* Instantiates by path
		* @returns object
		*/
		function instantiate($path){
			if(!is_file("$path.php")) return false;
			$class = array_pop(explode('/', $path));
			require_once "$path.php";
			return new $class;
		}	
		
####	CLIENT SALT
		
		/**
		* Creates salt unique to client {@link sm_client_id}
		* @returns string
		*/
		function mkSalt($len=64){
			$salt = rand_string(max(intval($len), 64));
			Cache::write('saltpot/'.sm_client_id(), $salt);
			return $salt;
		}
		
		/**
		* Retrieves above on second hit for comparison with hashed pass
		* @returns string
		*/
		private function getSalt(){
			Cache::clearCache("saltpot", 300);
			return Cache::get('saltpot/'.sm_client_id());
		}
		
####	EVENTS

		/**
		* Registers a function to be called during event
		*
		* @param string event
		* @param callback function
		*/
		function register_event_func($event, $func){
			$this->event_funcs[$event][] = $func;	
		}
		
		/**
		* Fires an event
		*
		* @param string event
		*/
		function fire_event($event){
			if(!isset($this->event_funcs[$event])) return;
			foreach($this->event_funcs[$event] as $func) call_user_func($func);
			unset($this->event_funcs[$event]);
		}

####	AUTHENTICATION
		
		/**
		* Current user, if any
		*
		* @returns SmRoot_User
		*/
		function usr(){
			return $this->root_pkg()->usr();	
		}
		
		/**
		* Current session, if any
		*
		* @returns SmRoot_Session
		*/
		function session(){
			return $this->root_pkg()->session();	
		}
		
		/**
		* Root package getter
		*
		* @returns Package_SmRoot
		*/
		function root_pkg(){
			return $this->proxy('root')->package('SmRoot');	
		}
		
		/**
		* Opens a session for user with sent hash
		*
		* @param string username
		* @param string hashed password and salt
		* @returns SmRoot_Session
		*/
		function open_session($uname, $sha1){
			if(!$salt = $this->getSalt()) return false;
			return $this->root_pkg()->open_session($uname, $sha1, $salt);
		}

		/**
		* Restores a session if there is a valid one
		*
		* @returns SmRoot_Session
		*/
		function restore_session(){
			return $this->root_pkg()->restore_session();
		}
		
		/**
		* Closes a session
		*
		* @returns bool
		*/
		function close_session(){
			return $this->root_pkg()->close_session();
		}
	}


